package CCNQ::Billing::CallRecords;
# Copyright (C) 2006, 2007  Stephane Alnet
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 

#
# For more information visit http://carrierclass.net/
#
use strict; use warnings;
use Fatal qw(open close);

use base qw(CCNQ::Object);

use DBI;

sub _init
{
    my $self = shift;
    $self->{records} = {};
    $self->{ignore} = {};
    $self->{err} = $self->open_err;
}

sub warn { my $self = shift; $self->{err}->print(@_); }

sub ignore_callref($)
{
    my $self = shift;
    my $callref = shift;
    
    return exists $self->{ignore}->{$callref};
}

sub set_ignore_callref($)
{
    my $self = shift;
    my $callref = shift;
    
    $self->{ignore}->{$callref} = 1;
}


=pod
    retrieve_partial_record($callref)
    register_incomplete_record($callref,$record)
    mark_record_completed($callref,$record)
    
        A record processing instance will first pickup a record from its Call-ID,
        then either return it to the pool, or mark it as completed.
=cut

sub retrieve_partial_record
{
    my $self = shift;
    my ($callref) = @_;
    if(not defined $callref)
    {
        $self->warn('No callref');
    }
    else
    {
        return $self->{records}->{$callref} if exists $self->{records}->{$callref};
    }
    return {};
}

sub register_incomplete_record
{
    my $self = shift;
    my ($callref,$record) = @_;
    $self->{records}->{$callref} = $record;
}

sub mark_record_completed
{
    my $self = shift;
    my ($callref) = @_;
    $self->set_ignore_callref($callref);
    $self->{records}->{$callref} = undef;
    delete $self->{records}->{$callref};
}

=pod

    output_incomplete
    
        At the end of the processing of all the records, some records
        may remain incomplete (e.g. start but no stop, etc.).
        These records are reported as errors and printed out.
        
=cut

sub output_incomplete
{
    my $self = shift;

    $self->warn ("<h1>Listing incomplete records</h1>\n");
    
    my @fields = qw( start stop callref src dst src_type dst_type billable );

    $self->warn ("<table><tr>",join('',map{"<th>$_</th>"} @fields),"</tr>\n");
    
    while( my ($callref,$record) = each %{$self->{records}})
    {
        $self->warn(
            "<tr>",
            join('', map { "<td>".($record->{$_}||'')."</td>" } @fields),
            "</tr>\n"
            )
            unless $self->ignore_callref($callref);
    }
    $self->warn ("</table>\n");
}

=pod

    run
    
        Processes raw records; uses parse_raw() and process_record() provided
        by the instantiated class to handle the actual data.

=cut

sub run
{
    my $self = shift;
    
    my @field_names = $self->_field_names;

    my $started_at = time();

    my $counted = 0;
    my $processed = 0;

    for my $file (@_)
    {
        my $fh;
        if($file =~ /\.bz2$/)
        {
            open($fh, "bzcat '$file' |");
        }
        elsif($file =~ /\.gz$/)
        {
            open($fh, "zcat '$file' |");
        }
        else
        {
            open($fh, '<', $file);
        }

        while(<$fh>)
        {
            chomp;
            $self->{_line} = $_;
            my @fields = $self->parse_raw($_);
            my %fields = map { $field_names[$_] => $fields[$_] } (0..$#fields);
            $counted++;
            $processed += $self->process_record(%fields);
        }
        close($fh);
    }

    $self->warn ("<h1>Processed/Counted: $processed/$counted</h1>\n");
    $self->output_incomplete;
}

1;
